"""
DPoP Token 自动生成器
用于自动生成和刷新 Mercari API 所需的 DPoP (Demonstration of Proof-of-Possession) 令牌
"""

import json
import time
import uuid
import base64
import hashlib
import secrets
from typing import Dict, Optional, Tuple
from datetime import datetime, timedelta
import logging
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
import jwt

# 配置日志
logger = logging.getLogger(__name__)


class DPoPTokenGenerator:
    """
    DPoP Token 生成器
    
    DPoP (Demonstration of Proof-of-Possession) 是一种 OAuth 2.0 安全扩展，
    用于将访问令牌绑定到特定的客户端实例。
    """
    
    def __init__(self, private_key: Optional[str] = None, public_key: Optional[str] = None):
        """
        初始化 DPoP Token 生成器
        
        Args:
            private_key: EC P-256 私钥（PEM格式），如果不提供则自动生成
            public_key: EC P-256 公钥（PEM格式），如果不提供则从私钥导出
        """
        if private_key and public_key:
            self.private_key_pem = private_key
            self.public_key_pem = public_key
            self._load_keys_from_pem()
        else:
            self._generate_key_pair()
        
        # 生成设备UUID（用于标识客户端实例）
        self.device_uuid = str(uuid.uuid4())
        logger.info(f"🔑 DPoP Token生成器初始化完成，设备UUID: {self.device_uuid}")
    
    def _generate_key_pair(self) -> None:
        """生成 EC P-256 密钥对"""
        try:
            # 生成 EC P-256 私钥
            self.private_key = ec.generate_private_key(ec.SECP256R1())
            self.public_key = self.private_key.public_key()
            
            # 转换为 PEM 格式
            self.private_key_pem = self.private_key.private_bytes(
                encoding=Encoding.PEM,
                format=PrivateFormat.PKCS8,
                encryption_algorithm=NoEncryption()
            ).decode('utf-8')
            
            self.public_key_pem = self.public_key.public_bytes(
                encoding=Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            ).decode('utf-8')
            
            logger.info("🔐 成功生成新的 EC P-256 密钥对")
            
        except Exception as e:
            logger.error(f"❌ 生成密钥对失败: {e}")
            raise
    
    def _load_keys_from_pem(self) -> None:
        """从 PEM 格式加载密钥"""
        try:
            self.private_key = serialization.load_pem_private_key(
                self.private_key_pem.encode('utf-8'),
                password=None
            )
            self.public_key = serialization.load_pem_public_key(
                self.public_key_pem.encode('utf-8')
            )
            logger.info("🔓 成功加载现有密钥对")
            
        except Exception as e:
            logger.error(f"❌ 加载密钥失败: {e}")
            raise
    
    def _get_public_key_jwk(self) -> Dict:
        """
        获取公钥的 JWK (JSON Web Key) 格式
        
        Returns:
            JWK 格式的公钥
        """
        try:
            # 获取公钥的坐标
            public_numbers = self.public_key.public_numbers()
            
            # 将坐标转换为字节并进行 base64url 编码
            x_bytes = public_numbers.x.to_bytes(32, byteorder='big')
            y_bytes = public_numbers.y.to_bytes(32, byteorder='big')
            
            x_b64 = base64.urlsafe_b64encode(x_bytes).decode('utf-8').rstrip('=')
            y_b64 = base64.urlsafe_b64encode(y_bytes).decode('utf-8').rstrip('=')
            
            jwk = {
                "crv": "P-256",
                "kty": "EC",
                "x": x_b64,
                "y": y_b64
            }
            
            return jwk
            
        except Exception as e:
            logger.error(f"❌ 生成JWK失败: {e}")
            raise
    
    def generate_dpop_token(self, 
                           http_method: str = "POST",
                           http_uri: str = "https://api.mercari.jp/v2/entities:search",
                           expires_in: int = 3600) -> str:
        """
        生成 DPoP Token
        
        Args:
            http_method: HTTP 方法（如 POST, GET）
            http_uri: 请求的完整 URI
            expires_in: 令牌有效期（秒），默认1小时
            
        Returns:
            DPoP Token 字符串
        """
        try:
            # 当前时间戳
            now = int(time.time())
            
            # JWT Header
            header = {
                "typ": "dpop+jwt",
                "alg": "ES256",
                "jwk": self._get_public_key_jwk()
            }
            
            # JWT Payload
            payload = {
                "iat": now,  # 签发时间
                "jti": str(uuid.uuid4()),  # JWT ID（唯一标识符）
                "htu": http_uri,  # HTTP URI
                "htm": http_method,  # HTTP Method
                "uuid": self.device_uuid  # 设备UUID
            }
            
            # 如果设置了过期时间
            if expires_in > 0:
                payload["exp"] = now + expires_in
            
            # 使用私钥签名生成 JWT
            token = jwt.encode(
                payload=payload,
                key=self.private_key,
                algorithm="ES256",
                headers=header
            )
            
            logger.info(f"✅ 成功生成DPoP Token，有效期: {expires_in}秒")
            logger.debug(f"Token payload: {payload}")
            
            return token
            
        except Exception as e:
            logger.error(f"❌ 生成DPoP Token失败: {e}")
            raise
    
    def generate_fresh_token(self) -> str:
        """
        生成新的 DPoP Token（使用默认参数）
        
        Returns:
            新的 DPoP Token
        """
        return self.generate_dpop_token()
    
    def is_token_expired(self, token: str) -> bool:
        """
        检查 Token 是否已过期
        
        Args:
            token: DPoP Token
            
        Returns:
            True 如果已过期，False 如果仍有效
        """
        try:
            # 解码 JWT（不验证签名，只检查过期时间）
            payload = jwt.decode(token, options={"verify_signature": False})
            
            if "exp" in payload:
                exp_time = payload["exp"]
                current_time = int(time.time())
                
                # 提前5分钟判断为过期，确保有足够的缓冲时间
                buffer_time = 300  # 5分钟
                is_expired = current_time >= (exp_time - buffer_time)
                
                if is_expired:
                    logger.warning(f"⚠️ DPoP Token即将过期或已过期，过期时间: {datetime.fromtimestamp(exp_time)}")
                
                return is_expired
            else:
                # 如果没有过期时间，认为永不过期
                return False
                
        except Exception as e:
            logger.error(f"❌ 检查Token过期状态失败: {e}")
            return True  # 出错时认为已过期，触发重新生成
    
    def get_key_pair_info(self) -> Dict[str, str]:
        """
        获取密钥对信息（用于持久化存储）
        
        Returns:
            包含私钥和公钥的字典
        """
        return {
            "private_key": self.private_key_pem,
            "public_key": self.public_key_pem,
            "device_uuid": self.device_uuid
        }
    
    def decode_token_info(self, token: str) -> Optional[Dict]:
        """
        解码 Token 信息（用于调试）
        
        Args:
            token: DPoP Token
            
        Returns:
            Token 的 payload 信息
        """
        try:
            payload = jwt.decode(token, options={"verify_signature": False})
            
            # 添加可读的时间信息
            if "iat" in payload:
                payload["iat_readable"] = datetime.fromtimestamp(payload["iat"]).isoformat()
            if "exp" in payload:
                payload["exp_readable"] = datetime.fromtimestamp(payload["exp"]).isoformat()
                payload["expires_in_seconds"] = payload["exp"] - int(time.time())
            
            return payload
            
        except Exception as e:
            logger.error(f"❌ 解码Token信息失败: {e}")
            return None


# 创建默认实例
default_dpop_generator = DPoPTokenGenerator()


def generate_mercari_dpop_token() -> str:
    """
    便捷函数：生成 Mercari API 专用的 DPoP Token
    
    Returns:
        DPoP Token 字符串
    """
    return default_dpop_generator.generate_dpop_token(
        http_method="POST",
        http_uri="https://api.mercari.jp/v2/entities:search",
        expires_in=86400  # 24小时有效期
    )


def is_mercari_token_expired(token: str) -> bool:
    """
    便捷函数：检查 Mercari DPoP Token 是否过期
    
    Args:
        token: DPoP Token
        
    Returns:
        True 如果已过期，False 如果仍有效
    """
    return default_dpop_generator.is_token_expired(token)


def get_fresh_mercari_token() -> str:
    """
    便捷函数：获取新的 Mercari DPoP Token
    
    Returns:
        新的 DPoP Token
    """
    logger.info("🔄 正在生成新的 Mercari DPoP Token...")
    token = generate_mercari_dpop_token()
    logger.info("✅ 新的 Mercari DPoP Token 生成完成")
    return token


if __name__ == "__main__":
    # 测试代码
    print("🧪 测试 DPoP Token 生成器...")
    
    # 生成新的 Token
    token = get_fresh_mercari_token()
    print(f"生成的 Token: {token[:50]}...")
    
    # 解码 Token 信息
    token_info = default_dpop_generator.decode_token_info(token)
    if token_info:
        print(f"Token 信息: {json.dumps(token_info, indent=2, ensure_ascii=False)}")
    
    # 检查过期状态
    is_expired = is_mercari_token_expired(token)
    print(f"Token 是否过期: {is_expired}")
    
    print("✅ 测试完成")
